package serverside;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class ATM implements Runnable {

    //class members 
    Socket clientConnectionSocket;
    BufferedReader inFromClient = null;
    DataOutputStream outToClient = null;
    ATM[] myThreads;
    String clientSentence = "null";
    String transactionResult;
    int index;

    private boolean userAuthenticated; // wheter user is authenticated
    private int currentCardID; //current user's account number    
    private BankDatabase bankDatabase; //account information database   
    private CashDispenser cashDispenser; //ATM's cash dispenser  
    private Screen screen;
    private Keypad keypad;
    //some constants for make work easier 
    private static final String BALANCE_INQUIRY = "BALANCEINQUIRY";
    private static final String WITHDRAWAL = "WITHDRAW";
    private static final String DEPOSIT = "CASHDEPOSIT";
    private static final String FUNDTRANSFER = "FUNDTRANSFER";
    private static final String BILLPAYMENT = "BILLPAYMENT";
    private static final String CHANGEPIN = "CHANGEPIN";
    private static final String EXIT = "EXIT";
    //

    int i = 0;
    int j = 0;
    String[] list;

    public ATM(Socket _connectionSocket, ATM[] _threads, int _index) {
        clientConnectionSocket = _connectionSocket;
        myThreads = _threads;
        index = _index;
        initializeStreams();
        initializeMember();

        System.out.println("Client " + clientConnectionSocket.getInetAddress()
                + " is connected");
    }//end of constructor 

    // <editor-fold defaultstate="collapsed" desc="Impotnant metods">    
    public static String[] separate(String x) {
        int temp = 0;
        String[] str = new String[4];
        for (int i = 0; i < 4; i++) {
            str[i] = "";
        }
        for (char ch : x.toCharArray()) {

            if (ch == ',') {
                temp++;
                continue;
            }
            str[temp] = str[temp] + ch;
        }
        return str;
    }//end method separate

    private void initializeStreams() {
        try {
            inFromClient = new BufferedReader(
                    new InputStreamReader(clientConnectionSocket.getInputStream()));
            outToClient = new DataOutputStream(clientConnectionSocket.getOutputStream());
        } catch (IOException ex) {
            //if an exception occure then there will be some errors with the
            // input and/or output stream
            System.err.println("Error in initializeStreams method ");
            ex.printStackTrace();
        }
    }//end method initializeStreams

    private void initializeMember() {
        userAuthenticated = false; // user is not authenticated to start
        currentCardID = 0; // no current account number to start
        bankDatabase = new BankDatabase(); // create account info database 
        cashDispenser = new CashDispenser();
        screen = new Screen(outToClient);
        keypad = new Keypad(inFromClient);
    }
// </editor-fold>

    @Override
    public void run() {
        // tell the client that he is connected to server                 
        screen.sendDataToClient("You are connected to ATM server");

        while (true) {
            //loop while user is not yet authenticated
            while (!userAuthenticated) {
                clientSentence = keypad.getInput();
                System.out.println("Client " + clientConnectionSocket
                        + "sent " + clientSentence);
                if (clientSentence.equalsIgnoreCase("LOGIN")) {
                    authenticateUser();// authinticateUser
                }
            }//end while

            performTransactions();// user is now authenticated 
            userAuthenticated = false; //reset before next ATM session
            currentCardID = 0; // reset before next ATM session 
        }//end while loop
    }//end run()

    //attmpts to authenticate user aganist database 
    private void authenticateUser() {
        screen.sendDataToClient("You can send password and PIN number.");
        //wait for client data; cardID and PIN
        clientSentence = keypad.getInput();

        System.out.println("Client " + clientConnectionSocket
                + "sent " + clientSentence);

        list = separate(clientSentence);
        System.out.println(list.toString());

        int cardID = Integer.parseInt(list[0]);
        int pin = Integer.parseInt(list[1]);

        // set userAuthenticated to boolean value returend by database
        userAuthenticated
                = bankDatabase.login(cardID, pin);

        // check whether authentication succeeded
        if (userAuthenticated) {
            currentCardID = cardID;
            screen.sendDataToClient("true");
            System.out.println("Sent True:: User successfully logedin\n");
        }//end if
        else {
            screen.sendDataToClient("false");
            System.out.println("Sent False:: user sent wrong card id or PIN\n");
        }
    }//end method authenticateUser

    // display main menu and perform transaction 
    private void performTransactions() {
        // local variable to store transaction currently being processed
        Transaction currentTransaction = null;
        clientSentence = keypad.getInput();

        System.out.println("Client " + clientConnectionSocket
                + "sent " + clientSentence);

        boolean userExited = false; // user has not choosen to exit
        if (clientSentence.equalsIgnoreCase("EXIT")) {
            userExited = true;
        }

        //loop while user has not choosen option to exit system
        while (!userExited) {
            //decide how to proceed based on user's menu selection 
            switch (clientSentence) {
                //user choose to perform one of the three transaction types
                case BALANCE_INQUIRY:
                case WITHDRAWAL:
                case DEPOSIT:
                case FUNDTRANSFER:
                case BILLPAYMENT:
                case CHANGEPIN:
                    //initialize as a new object of choosen type
                    currentTransaction = createTransaction(clientSentence);
                    currentTransaction.execute(); // execute transaction 
                    break;
                case EXIT: //user choose to terminate session
                    System.out.println("\nClient Exiting the system...");
                    userExited = true;// this ATM session should end 
                    break;
                default: // yser did not enter an integer from 1-4
                    System.out.println(
                            "\nYou did not enter a valid selection. Try again.");
                    break;
            }//end switch
            clientSentence = keypad.getInput();

            System.out.println("Client " + clientConnectionSocket
                    + "sent " + clientSentence);
        }//end while
    }//end method performTransactions

    private Transaction createTransaction(String type) {
        Transaction temp = null; // temporary Transaction variable

        //determin which type of transaction to create
        switch (type) {
            case BALANCE_INQUIRY: // create new BalanceInquiry transaction
                temp = new BalanceInquiry(
                        currentCardID, bankDatabase, screen);
                break;
            case WITHDRAWAL: // create new Withdrawal transaction                                 
                temp = new Withdrawal(
                        currentCardID, bankDatabase,
                        screen, keypad, cashDispenser);
                break;
            case DEPOSIT: //create new Deposit transaction 
                temp = new Deposit(currentCardID, bankDatabase,
                        screen, keypad);
                break;
            case FUNDTRANSFER:
                temp = new FundTransfer(currentCardID, bankDatabase,
                        screen, keypad);
                break;
            case BILLPAYMENT:
                temp = new BillPayment(currentCardID, bankDatabase,
                        screen, keypad);
                break;
            case CHANGEPIN:
                temp = new BillPayment(currentCardID, bankDatabase,
                        screen, keypad);
                break;
        }//end switch

        return temp; //return tne newly created object
    }//end method createTransaction

}//
